package com.sikaryofficial.backend.manager.export;

import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.sikaryofficial.backend.constant.DownloadTaskStatusEnum;
import com.sikaryofficial.backend.constant.DownloadTaskTypeEnum;
import com.sikaryofficial.backend.domain.mapping.UserReportMapping;
import com.sikaryofficial.backend.domain.vo.export.UserDailyExportVO;
import com.sikaryofficial.backend.domain.vo.export.UserRegisterExportVO;
import com.sikaryofficial.backend.service.IDownloadTaskService;
import com.sikaryofficial.common.core.domain.R;
import com.sikaryofficial.common.core.exception.ServiceException;
import com.sikaryofficial.common.core.utils.SpringUtils;
import com.sikaryofficial.common.redis.lock.RedisDistributedLocker;
import com.sikaryofficial.common.redis.service.RedisService;
import com.sikaryofficial.common.security.utils.SecurityUtils;
import com.sikaryofficial.system.api.RemoteFileService;
import com.sikaryofficial.system.api.domain.SysFile;
import com.sikaryofficial.system.api.model.ManualExportReq;
import com.sikaryofficial.system.api.model.user.UserDailyDTO;
import com.sikaryofficial.system.api.model.user.UserRegisterDTO;
import com.univocity.parsers.common.processor.BeanWriterProcessor;
import com.univocity.parsers.csv.CsvWriter;
import com.univocity.parsers.csv.CsvWriterSettings;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.text.MessageFormat;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import static com.sikaryofficial.backend.constant.RedisCacheKey.*;
import static com.sikaryofficial.common.core.utils.file.FileUtils.fileToMultipartFile;

@Service
@Slf4j
public abstract class AbstractExportManager implements ExportManager {
    @Autowired
    protected RedisDistributedLocker locker;
    @Autowired
    protected IDownloadTaskService downloadTaskService;
    @Autowired
    protected RedisService redisService;
    @Resource(name = "asyncTaskExecutor")
    protected ThreadPoolTaskExecutor asyncTaskExecutor;

    public static void writerTwo(List<UserDailyDTO> userDailyDTOS, String filePath) throws IOException {
        // csvWriter
        CsvWriterSettings settings = new CsvWriterSettings();
        settings.setRowWriterProcessor(new BeanWriterProcessor<>(UserDailyExportVO.class));
        CsvWriter csvWriter = new CsvWriter(Files.newOutputStream(new File(filePath).toPath()), settings);
        // header
        csvWriter.writeHeaders();
        // data
        csvWriter.processRecords(UserReportMapping.INSTANCE.coverToDailyVOList(userDailyDTOS));
        // 刷新缓存
        csvWriter.flush();
    }

    @Override
    public void createWinnerFileById(Long activityId) {
        // 设置锁key
        String lockKey = MessageFormat.format(ACTIVITY_WINNER_EXPORT_LOCK, activityId.toString());
        if (locker.tryLock(lockKey, TimeUnit.MINUTES, 2, 2)) {
            log.info("获取锁成功, activityId: {}", activityId);
            try {
                Long userId = SecurityUtils.getUserId();
                String qrcodeStatusKey = MessageFormat.format(ACTIVITY_WINNER_EXPORT_STATUS_CACHE, activityId.toString());
                redisService.setCacheObject(qrcodeStatusKey, DownloadTaskStatusEnum.GENERATING.getCode(), 1L, TimeUnit.MINUTES);
                String remark = buildExportFileName(activityId);
                Long taskId = IdWorker.getId();
                downloadTaskService.initDownloadWithRemark(userId, activityId, taskId, getTaskTypeCode(), remark);
                String taskFolderPath = getTaskFolderPath(taskId);
                File folder = new File(taskFolderPath);
                folder.mkdirs();
                genFile(activityId, folder);
                updateTaskStatusAsync(userId, activityId, taskId, folder, qrcodeStatusKey);
            } catch (Exception e) {
                log.error("生成文件失败, activityId: {}, 错误信息: {}", activityId, e.getMessage(), e);
                throw new ServiceException("生成文件失败: " + e.getMessage());
            } finally {
                log.info("释放锁, activityId: {}", activityId);
                locker.unlock(lockKey);
            }
        } else {
            log.info("获取锁失败, activityId: {}", activityId);
            throw new ServiceException("文件正在生成中，请稍后重试");
        }
    }

    public String buildExportFileName(Long activityId) {
        String remark = "";
        DownloadTaskTypeEnum downloadTaskTypeEnum = DownloadTaskTypeEnum.getByCode(getTaskTypeCode());
        switch (downloadTaskTypeEnum) {
            case ACTIVITY_WINNER:
            case ACTIVITY_ATTEND_DETAIL:
            case PRIZE_WINNER:
                remark = downloadTaskTypeEnum.getDesc().concat("_").concat(activityId.toString());
                break;
            default:
                break;
        }
        return remark;
    }

    private String generateCacheKey(ManualExportReq req) {
        return DigestUtil.md5Hex(req.getStartTime() + "_"
                + req.getEndTime()
        );
    }

    @Override
    public void createFileByParam(ManualExportReq req) {
        // 设置锁key
        String md5 = generateCacheKey(req);
        Long objectId = IdWorker.getId();
        String lockKey = MessageFormat.format(EXPORT_STATUS_PREFIX_LOCK, md5);
        if (locker.tryLock(lockKey, TimeUnit.MINUTES, 2, 2)) {
            log.info("获取锁成功, md5: {}", md5);
            try {
                Long userId = SecurityUtils.getUserId();
                String qrcodeStatusKey = MessageFormat.format(EXPORT_STATUS_PREFIX_CACHE, md5);
                redisService.setCacheObject(qrcodeStatusKey, DownloadTaskStatusEnum.GENERATING.getCode(), 1L, TimeUnit.MINUTES);
                Long taskId = IdWorker.getId();
                downloadTaskService.initDownloadWithRemark(userId, objectId, taskId, getTaskTypeCode(), buildExportFileName(req));
                String taskFolderPath = getTaskFolderPath(taskId);
                File folder = new File(taskFolderPath);
                folder.mkdirs();
                genFile(req, objectId, folder);
                updateTaskStatusAsync(userId, objectId, taskId, folder, qrcodeStatusKey);
            } catch (Exception e) {
                log.error("生成文件失败, activityId: {}, 错误信息: {}", objectId, e.getMessage(), e);
                throw new ServiceException("生成文件失败: " + e.getMessage());
            } finally {
                log.info("释放锁, activityId: {}", objectId);
                locker.unlock(lockKey);
            }
        } else {
            log.info("获取锁失败, activityId: {}", objectId);
            throw new ServiceException("文件正在生成中，请稍后重试");
        }
    }

    private String buildExportFileName(ManualExportReq req) {
        String remark = "";
        DownloadTaskTypeEnum downloadTaskTypeEnum = DownloadTaskTypeEnum.getByCode(getTaskTypeCode());
        String startDateStr = req.getStartTime().substring(0, 10);
        String endDateStr = req.getEndTime().substring(0, 10);
        switch (downloadTaskTypeEnum) {
            case REGISTER_USER:
            case ACTIVITY_DAILY:
                remark = downloadTaskTypeEnum.getDesc().concat("_").concat(startDateStr).concat("_")
                        .concat(endDateStr);
                break;
            default:
                break;
        }
        return remark;
    }

    public void updateTaskStatusAsync(Long userId, Long activityId, Long taskId, File folder, String qrcodeStatusKey) {
        CompletableFuture.runAsync(() -> {
            try {
                String genLocalZipFile = genLocalZipFile(folder, activityId, taskId);
                log.info("本地文件路径: {}", genLocalZipFile);
                RemoteFileService remoteFileService = SpringUtils.getBean(RemoteFileService.class);
                R<SysFile> sysFileR = remoteFileService.upload(fileToMultipartFile(new File(genLocalZipFile)));
                String downloadTaskFileUrl = "";
                if (Objects.nonNull(sysFileR) && Objects.nonNull(sysFileR.getData())) {
                    downloadTaskFileUrl = sysFileR.getData().getUrl();
                }
                downloadTaskService.updateDownloadTask(userId, taskId, downloadTaskFileUrl);
                redisService.deleteObject(qrcodeStatusKey);
            } catch (Exception e) {
                log.error("更新任务状态失败, activityId: {}, taskId: {}, 错误信息: {}", activityId, taskId, e.getMessage(), e);
            }
        }, asyncTaskExecutor);
    }

    /**
     * 写入数据到文件
     *
     * @param userDailyDTOS
     * @param filePath
     * @throws IOException
     */
    public void writerDataToFile(List<UserRegisterDTO> userDailyDTOS, String filePath) throws IOException {
        // csvWriter
        CsvWriterSettings settings = new CsvWriterSettings();
        settings.setRowWriterProcessor(new BeanWriterProcessor<>(UserRegisterExportVO.class));
        CsvWriter csvWriter = new CsvWriter(Files.newOutputStream(new File(filePath).toPath()), settings);
        // header
        csvWriter.writeHeaders();
        // data
        csvWriter.processRecords(UserReportMapping.INSTANCE.coverToRegisterVOList(userDailyDTOS));
        // 刷新缓存
        csvWriter.flush();
    }

}
